﻿using System;
using System.Collections.Generic;
using System.Text;

using MyLibrary.ThreeLayer.DataAccess.MetaData;
using MyLibrary.ThreeLayer.DataAccess.Interface;
using MyLibrary.ThreeLayer.DataAccess.Enum;
using MyLibrary.ThreeLayer.DataAccess.Attributes;

namespace MyLibrary.ThreeLayer.DataAccess.QueryLanguage
{

    public class Select : QueryExpression
    {
        internal readonly QueryField[] _queryFields;

        internal override SqlToken SQLToken
        {
            get { return SqlToken.Select; }
        }

        public Select(IList<QueryExpression> sqlList, params QueryField[] fields)
            : base(sqlList)
        {
            this._queryFields = fields;
        }

        public From From<T>()
        {
            return new From(base.SqlList, typeof(T));
        }

        public override string ToSQL()
        {
            string strCommand = String.Empty;
            if (this._queryFields == null || this._queryFields.Length == 0)
                strCommand = "*";
            else
            {
                QueryField[] queryFields = this._queryFields;
                for (int i = 0; i < queryFields.Length; i++)
                {
                    IQueryField field = queryFields[i];
                    if (strCommand == String.Empty)
                        strCommand += ",";
                    strCommand += "[" + field.FieldName + "]";
                }
            }

            return ("Select " + strCommand);
        }
    }

    public class Where : UnionableQueryExpression
    {
        internal readonly ConditionExpression _queryExpression;

        internal override SqlToken SQLToken
        {
            get { return SqlToken.Where; }
        }

        internal Where(IList<QueryExpression> sqlList, ConditionExpression expr)
            : base(sqlList)
        {
            this._queryExpression = expr;
        }

        public OrderBy OrderBy(QueryField field)
        {
            return new OrderBy(this.SqlList, field, false);
        }

        public OrderBy OrderBy(QueryField field, bool desc)
        {
            return new OrderBy(this.SqlList, field, desc);
        }

        public OrderBy OrderBy(Order order, params Order[] orders)
        {
            return new OrderBy(this.SqlList, order, orders);
        }

        public override string ToSQL()
        {
            return (" Where " + this._queryExpression.ToString());
        }
    }

    public class Union : QueryExpression
    {
        internal readonly QueryExpression _sqlExpression;

        internal Union(IList<QueryExpression> sqlList, QueryExpression expr)
            : base(sqlList)
        {
            this._sqlExpression = expr;
        }

        public override string ToSQL()
        {
            return (" Union " + this._sqlExpression.ToString());
        }

        internal override SqlToken SQLToken
        {
            get { return SqlToken.Union; }
        }
    }

    public class JoinPrepare
    {
        internal readonly JoinType _joinType;
        internal readonly IList<QueryExpression> _sqlList;
        internal readonly Type _targetType;

        internal JoinPrepare(IList<QueryExpression> sqlList, Type targetType, JoinType joinType)
        {
            this._sqlList = sqlList;
            this._joinType = joinType;
            this._targetType = targetType;
        }

        public Join On(IQueryField field1, IQueryField field2)
        {
            return new Join(this._sqlList, this._targetType, this._joinType, new JoinPoint(field1, field2));
        }
    }

    public class JoinPoint
    {
        public readonly IQueryField[] _JoinFields = new IQueryField[2];

        public JoinPoint(IQueryField field1, IQueryField field2)
        {
            this._JoinFields[0] = field1;
            this._JoinFields[1] = field2;
        }
    }

    public class Join : QueryExpression
    {
        internal readonly JoinType _joinType;
        internal JoinPoint _joinPoint;
        internal readonly Type _targetType;

        internal override SqlToken SQLToken
        {
            get { return SqlToken.Join; }
        }

        internal Join(IList<QueryExpression> sqlList, Type targetType, JoinType joinType, JoinPoint joinPoint)
            : base(sqlList)
        {
            this._targetType = targetType;
            this._joinType = joinType;
            this._joinPoint = joinPoint;
        }

        public JoinPrepare InnerJoin<T>()
        {
            return new JoinPrepare(base.SqlList, typeof(T), JoinType.InnerJoin);
        }

        public JoinPrepare LeftJoin<T>()
        {
            return new JoinPrepare(base.SqlList, typeof(T), JoinType.LeftJoin);
        }

        public JoinPrepare RightJoin<T>()
        {
            return new JoinPrepare(base.SqlList, typeof(T), JoinType.RightJoin);
        }

        public OrderBy OrderBy(QueryField field)
        {
            return new OrderBy(base.SqlList, field, false);
        }

        public OrderBy OrderBy(QueryField field, bool desc)
        {
            return new OrderBy(base.SqlList, field, desc);
        }

        public OrderBy OrderBy(Order order, params Order[] orders)
        {
            return new OrderBy(base.SqlList, order, orders);
        }

        public override string ToSQL()
        {
            MappingCollection mappingCollection1 = MetaDataFactory.GetMappingCollection(this._targetType);
            MappingCollection mappingCollection2 = MetaDataFactory.GetMappingCollection(this._joinPoint._JoinFields[0]);
            MappingCollection mappingCollection3 = MetaDataFactory.GetMappingCollection(this._joinPoint._JoinFields[1]);

            return (" Join [" + mappingCollection1._TableName + "].[" + mappingCollection2._TableName + "].[" + mappingCollection2.GetColumnName(this._joinPoint._JoinFields[0].FieldName) + "]=[" + mappingCollection3._TableName + "].[" + mappingCollection2.GetColumnName(this._joinPoint._JoinFields[1].FieldName) + "]");
        }

        public UnionableQueryExpression Where(ConditionExpression expression)
        {
            return new Where(base.SqlList, expression);
        }
    }

    public class Order
    {
        internal readonly bool _desc;
        internal readonly QueryField _orderField;

        public Order(QueryField field)
        {
            this._desc = false;
            this._orderField = field;
        }

        public Order(QueryField field, bool desc)
        {
            this._desc = desc;
            this._orderField = field;
        }
    }

    public class OrderBy : UnionableQueryExpression
    {
        internal readonly IList<Order> _orders;

        internal override SqlToken SQLToken
        {
            get { return SqlToken.OrderBy; }
        }

        public OrderBy(IList<QueryExpression> sqlList, Order order, params Order[] orders)
            : base(sqlList)
        {
            this._orders = new List<Order>();
            this._orders.Add(order);
            foreach (Order o in orders)
                this._orders.Add(o);
        }

        public OrderBy(IList<QueryExpression> sqlList, QueryField field, bool desc)
            : base(sqlList)
        {
            this._orders = new List<Order>();
            this._orders.Add(new Order(field, desc));
        }

        public override string ToSQL()
        {
            string strOrder = " Order By ";
            string columnName = String.Empty;
            foreach (Order order in this._orders)
            {
                MappingCollection collection = MetaDataFactory.GetMappingCollection(order._orderField.TargetType);
                columnName = collection.GetColumnName(order._orderField.FieldName);
                strOrder = strOrder + "[" + collection._TableName + "].[" + columnName + "] ";
                if (order._desc)
                    strOrder += " Desc ";
            }
            return strOrder;
        }
    }

    public class From : UnionableQueryExpression
    {
        internal readonly Type _targetType;

        internal override SqlToken SQLToken
        {
            get { return SqlToken.From; }
        }

        internal From(IList<QueryExpression> sqlList, Type targetType)
            : base(sqlList)
        {
            this._targetType = targetType;
            
        }

        public JoinPrepare InnerJoin<T>()
        {
            return new JoinPrepare(base.SqlList, typeof(T), JoinType.InnerJoin);
        }

        public JoinPrepare LeftJoin<T>()
        {
            return new JoinPrepare(base.SqlList, typeof(T), JoinType.LeftJoin);
        }

        public JoinPrepare RightJoin<T>()
        {
            return new JoinPrepare(base.SqlList, typeof(T), JoinType.RightJoin);
        }

        public override string ToSQL()
        {
            MappingCollection mappingCollection = MetaDataFactory.GetMappingCollection(this._targetType);
            return (" From [" + mappingCollection._TableName + "]"); 
        }

        public UnionableQueryExpression Where(ConditionExpression expression)
        {
            return new Where(base.SqlList, expression);
        }
    }

    public static class Function
    {
        public static QueryField Count(QueryField field)
        {
            field._Function = FunctionType.Count;
            return field;
        }

        public static QueryField Max(QueryField field)
        {
            field._Function = FunctionType.Max;
            return field;
        }
    }
}
